package com.zwl.swagger.plugins;

import com.fasterxml.classmate.ResolvedType;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;

import java.util.Set;

import static springfox.documentation.schema.Collections.collectionElementType;
import static springfox.documentation.schema.Collections.isContainerType;

/**
* @author zhangweilin
* @date 2021/10/27 20:44
* @Description:
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class ParameterTypeReaderPlus implements ParameterBuilderPlugin {
  private static final Logger LOGGER = LoggerFactory.getLogger(ParameterTypeReaderPlus.class);

  @Override
  public void apply(ParameterContext context) {
    context.parameterBuilder().parameterType(findParameterType(context));
  }

  @Override
  public boolean supports(DocumentationType delimiter) {
    return true;
  }

  public static String findParameterType(ParameterContext parameterContext) {
    ResolvedMethodParameter resolvedMethodParameter = parameterContext.resolvedMethodParameter();
    ResolvedType parameterType = resolvedMethodParameter.getParameterType();
    parameterType = parameterContext.alternateFor(parameterType);

    //Multi-part file trumps any other annotations
    if (isFileType(parameterType) || isListOfFiles(parameterType)) {
      return "form";
    }
    if (resolvedMethodParameter.hasParameterAnnotation(PathVariable.class)) {
      return "path";
    } else if (resolvedMethodParameter.hasParameterAnnotation(RequestBody.class)) {
      return "body";
    } else if (resolvedMethodParameter.hasParameterAnnotation(RequestPart.class)) {
      return "formData";
    } else if (resolvedMethodParameter.hasParameterAnnotation(RequestParam.class)) {
      return determineScalarParameterType(
          parameterContext.getOperationContext().consumes(),
          parameterContext.getOperationContext().httpMethod());
    } else if (resolvedMethodParameter.hasParameterAnnotation(RequestHeader.class)) {
      return "header";
    } else if (resolvedMethodParameter.hasParameterAnnotation(ModelAttribute.class)) {
      LOGGER.warn("@ModelAttribute annotated parameters should have already been expanded via "
          + "the ExpandedParameterBuilderPlugin");
      //注解RequestAttribute大量用于区域boss中，用于全局传值areaId,此时，此参数显示在文档参数上无意义，它什么类型都不是
    }else if (resolvedMethodParameter.hasParameterAnnotation(RequestAttribute.class)) {
      return "非接口参数，无须传递，前端可以忽略之，详情请与后端确认";
    }

    if (!resolvedMethodParameter.hasParameterAnnotations()) {
      return determineScalarParameterType(
          parameterContext.getOperationContext().consumes(),
          parameterContext.getOperationContext().httpMethod());
    }
    return "body";
  }

  private static boolean isListOfFiles(ResolvedType parameterType) {
    return isContainerType(parameterType) && isFileType(collectionElementType(parameterType));
  }

  private static boolean isFileType(ResolvedType parameterType) {
    return MultipartFile.class.isAssignableFrom(parameterType.getErasedType());
  }

  public static String determineScalarParameterType(Set<? extends MediaType> consumes, HttpMethod method) {
    String parameterType = "query";

    if (consumes.contains(MediaType.APPLICATION_FORM_URLENCODED)
            && method == HttpMethod.POST) {
      parameterType = "form";
    } else if (consumes.contains(MediaType.MULTIPART_FORM_DATA)
            && method == HttpMethod.POST) {
      parameterType = "formData";
    }

    return parameterType;
  }

}